# -*- coding: utf-8 -*-
# @Time    : 2024/6/28 17:56
# @Author  : yujiahao
# @File    : 04_numpy_nditer.py
# @description:NumPy遍历数组
import numpy as np


# NumPy 提供了一个 nditer 迭代器对象，它可以配合 for 循环完成对数组元素的遍历。


def main():
    # 使用 arange() 函数创建一个 3*4 数组，并使用 nditer 生成迭代器对象。

    a = np.arange(0, 60, 5)
    a = a.reshape(3, 4)

    print('原始数据\n', a)

    # todo 1、使用nditer迭代器,并使用for进行遍历

    for x in np.nditer(a):
        print('使用npiter遍历元素\t', x)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2、遍历顺序

    ''' 在内存中，Numpy 数组提供了两种存储数据的方式，分别是 C-order（行优先顺序）与 Fortrant-order（列优先顺序）。
        那么 nditer 迭代器又是如何处理具有特定存储顺序的数组呢？其实它选择了一种与数组内存布局一致的顺序，之所以这样做，是为了提升数据的访问效率。
    '''
    # 在默认情况下，当我们遍历数组中元素的时候，不需要考虑数组的存储顺序，这一点可以通过遍历上述数组的转置数组来验证。

    # a的转置数组(就是行变列)
    b = a.T
    print(b)
    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for x in np.nditer(b):
        # 这里看一下，虽然对原始数据进行转置，但是遍历出来的元素顺序元素还是不变，a 和 a.T 的遍历顺序是一样的，
        # 也就是说，它们在内存中的存储顺序是一样的。
        print(x, end=",")

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 在来看一下，使用C 样式访问转置数组的副本。（在 NumPy 中，copy 方法用于创建数组的副本。具体来说，a.T.copy(order='C') 会创建一个新的数组，而不是一个引用。
    # 这意味着修改副本不会影响原始数组，反之亦然。）
    # a.T.copy(order = 'C') 的遍历结果与示例 1、2 的数组遍历结果不一样。究其原因，就是因为它们在内存中的存储方式不一样。
    for i in np.nditer(b.copy(order='C')):
        print(i, end=",")

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 3、指定遍历顺序

    #  可以通过 nditer 对象的order参数来指定数组的遍历的顺序。

    a = np.arange(0, 60, 5)
    a = a.reshape(3, 4)

    print('默认打印:\n', a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for x in np.nditer(a, order='C'):
        print(x, end=",")

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for x in np.nditer(a, order='F'):
        print(x, end=",")

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 4、修改数组元素值

    '''
    nditer 对象提供了一个可选参数op_flags，它表示能否在遍历数组时对元素进行修改。它提供了三种模式，如下所示：
    
    1) read-only
        只读模式，在这种模式下，遍历时不能修改数组中的元素。
    2) read-write
        读写模式，遍历时可以修改元素值。
    3) write-only
        只写模式，在遍历时可以修改元素值。
    '''

    a = np.arange(0, 60, 5)
    a = a.reshape(3, 4)
    print("原数组是:\n", a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    for x in np.nditer(a, op_flags=['readwrite']):
        x[...] = 2 * x
    print('修改后的数组是：', a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 5、外部循环使用

    """
    nditer 对象的构造函数有一个 “flags” 参数，它可以接受以下参数值：

    flags 参数说明：
    参数值          描述说明
    c_index        可以跟踪 C 顺序的索引。
    f_index        可以跟踪 Fortran 顺序的索引。
    multi_index    每次迭代都会跟踪一种索引类型。
    external_loop  返回的遍历结果是具有多个值的一维数组。
    """

    a = np.arange(0, 60, 5)
    a = a.reshape(3, 4)
    print("原数组是:\n", a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 修改后数组
    for x in np.nditer(a, flags=['external_loop'], order='F'):
        print(x)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 6、 迭代多个数组

    #  如果两个数组都能够被广播，那么 nditer 对象就可以同时对它们迭代。【轮询小的数组】
    # 假设数组 a 的维度是 3*4，另一个数组 b 的维度是 1*4 （即维度较小的数组 b 可以被广播到数组 a 中）

    a = np.arange(0, 60, 5)
    a = a.reshape(3, 4)

    print("原数组是:\n", a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    b = np.array([1, 2, 3, 4], dtype=int)

    print("原数组是:\n", b)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 广播迭代
    for x, y in np.nditer([a, b]):
        print("%d:%d" % (x, y), end=",")


if __name__ == '__main__':
    main()
